In [5]:
class SimpleList:
    def __init__(self, data=None):
        if data is None:
            data = []
            
        self.data = list(data)
            
    def add(self, x):
        self.data.append(x)
        
    def __getitem__(self, idx):
        return self.data[idx]
    
    def __len__(self):
        return len(self.data)
    
    def sort(self):
        self.data.sort()

In [6]:
class SortedList(SimpleList):
    def __init__(self, data=None):
        super().__init__(data)
        self.sort()
        
    def add(self, x):
        super().add(x)
        self.sort()

In [24]:
class IntList(SimpleList):
    @staticmethod
    def _validate(x):
        if not isinstance(x, int):
            raise TypeError('{} is not an instance of int.'.format(x))
            
    def __init__(self, data=None):
        if data is not None:
            # Create list here to avoid emptying a one-time iterable
            data = list(data)
            
            for x in data:
                IntList._validate(x)
                
        super().__init__(data)
                
    def add(self, x):
        IntList._validate(x)
        super().add(x)

In [ ]:
class SortedIntList(SortedList, IntList):
    pass
Now we use `testmachine` to ensure that `SortedList` behaves as expected.

In [19]:
import random
import string
from testmachine import TestMachine
from testmachine.common import (basic_operations,
                                check,
                                generate,
                                lists,
                                operation)


def add_to_list(l, x):
    l = SortedList(l)
    l.add(x)
    return l


def random_string():
    return ''.join(random.sample(
        string.ascii_letters, 
        random.randint(1, len(string.ascii_letters))))


machine = TestMachine()
machine.add(
    basic_operations("sorted_lists"),
    
    # We need to be able to create random strings.
    generate(lambda _: random_string(),  target="strings"),
    
    # And lists of random strings.
    lists(source="strings", target="strlists"),
    
    # And empty SortedLists
    generate(lambda _: SortedList(), "sorted_lists"),
    
    # We construct SortedLists from lists of strings.
    operation(
        function=lambda d: SortedList(d),
        argspec=('strlists',),
        target='sorted_lists',
        pattern='SortedList({0})'),

    # And we add random strings to sorted lists
    operation(
        argspec=("sorted_lists", "strings"),
        target="sorted_lists",
        function=add_to_list,
        pattern='{0}.add({1})'),
    
    # Always checking that SortedLists stay sorted.
    check(
        test=lambda l: list(l) == sorted(list(l)),
        argspec=("sorted_lists",),
        pattern="assert {0} is sorted")
)

print('Testing SortedList')
machine.run()


Testing SortedList
Unable to find a failing program of length <= 200 after 500 iterations

In [27]:
import random
import string
from testmachine import TestMachine
from testmachine.common import (basic_operations,
                                check,
                                generate,
                                ints,
                                lists,
                                operation)


def create_int_list(d):
    try:
        return IntList(d)
    except TypeError:
        return IntList()
    

def add_to_int_list(l, x):
    try:
        l = IntList(l)
        l.add(x)
    except TypeError:
        pass
    
    return l


machine = TestMachine()
machine.add(
    basic_operations("sorted_lists"),
    
    # We want a bunch of ints
    ints(),
    lists(source="ints", target="int_lists"),
    
    # and floats
    generate(lambda _: random.random() * 1000, "floats"),
    lists(source="floats", target="float_lists"),
    
    # And empty IntLists
    generate(lambda _: IntList(), "IntLists"),
    
    # We construct IntLists from lists of ints
    operation(
        function=lambda d: IntList(d),
        argspec=('int_lists',),
        target='IntLists',
        pattern='IntList({0})'),

    # And from lists of floats (which fail, of course.)
    operation(
        function=create_int_list,
        argspec=('float_lists',),
        target='IntLists',
        pattern='IntList({0})'),
    
    
    # And we add ints to IntLists
    operation(
        argspec=("IntLists", "ints"),
        target="IntLists",
        function=add_to_int_list,
        pattern='{0}.add({1})'),
    
    # And we add floats.
    operation(
        argspec=("IntLists", "floats"),
        target="IntLists",
        function=add_to_int_list,
        pattern='{0}.add({1})'),
    
    # Always checking that IntLists contain only integers.
    check(
        test=lambda l: all(isinstance(x, float) for x in l),
        argspec=("IntLists",),
        pattern="assert {0} is all integers.")
)

print('Testing IntList')
machine.run()


Testing IntList
t1 = -9
t2 = <__main__.IntList object at 0x7f2e244abed0>
t3 = t2.add(t1)
assert t3 is all integers.
Traceback (most recent call last):
  File "/export/r832-1/store/sandbox/testmachine/venv/lib/python3.3/site-packages/testmachine-0.0.3-py3.3.egg/testmachine/testmachine.py", line 235, in run
    self.run_program(minimal)
  File "/export/r832-1/store/sandbox/testmachine/venv/lib/python3.3/site-packages/testmachine-0.0.3-py3.3.egg/testmachine/testmachine.py", line 283, in run_program
    context.run_program(program)
  File "/export/r832-1/store/sandbox/testmachine/venv/lib/python3.3/site-packages/testmachine-0.0.3-py3.3.egg/testmachine/testmachine.py", line 87, in run_program
    self.execute(operation)
  File "/export/r832-1/store/sandbox/testmachine/venv/lib/python3.3/site-packages/testmachine-0.0.3-py3.3.egg/testmachine/testmachine.py", line 92, in execute
    operation.invoke(self)
  File "/export/r832-1/store/sandbox/testmachine/venv/lib/python3.3/site-packages/testmachine-0.0.3-py3.3.egg/testmachine/operations.py", line 174, in invoke
    assert self.test(*args)
AssertionError
Out[27]:
RunContext(IntLists=2, ints=1)

In [ ]: